/*
 * @Author: 任诗韵 mlqs_1010@qq.com
 * @Date: 2025-06-27 10:17:26
 * @LastEditors: 任诗韵 mlqs_1010@qq.com
 * @LastEditTime: 2025-06-29 09:39:34
 * @FilePath: \Test3\Calculator.java
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */

 //继承JFrame 为了直接复用Swing框架中窗口管理能力
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.sound.sampled.*;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder; 
import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;

// 为了子类可以直接调用 JFrame public 的方法
import java.text.SimpleDateFormat;
import java.util.Date;

// ActionListener 是java中事件处理机制的核心部分
// 为了建立计算器组件和用户操作之间的响应机制
// 继承 JFrame 为了直接复用swing框架中窗口管理的能力
public class Calculator extends JFrame implements  ActionListener {
// 声明界面组件
  // 上下两个容器面板 文本框区域 按钮区域
    private JPanel north,center;
    // 文本框 显示计算过程、结果或者日期时间
    private JTextField field;
    // 按钮
    private JButton[] btns;
    // 新增：存储历史计算结果
    private String lastResult = "0";
    //判断是否处于科学计算器
    private boolean isInScientificMode = false;
    private Clip backgroundMusic; // 背景音乐播放器
     // 定义科学功能按钮的文本（用于区分普通按钮）
    private final String[] SCIENTIFIC_BUTTONS = {
        "sin", "cos", "tan", "log", "sqrt", "π","(",")"
    };

// 初始化窗口 - 构造器 构造方法 无参构造器
    public Calculator(){
        // 设置窗口title
        setTitle("科学计算器");
         // 设置窗口大小
        setSize(700,700);
         // 设置窗口关闭时程序
        setDefaultCloseOperation(EXIT_ON_CLOSE);
         // 设置窗口居中显示
        setLocationRelativeTo(null);
         // 初始化界面组件
        initComponent();
        // 新增：调用背景音乐播放方法
        playBackgroundMusic(); 
         // 设置窗口显示
        setVisible(true);
    }
    // 初始化界面组件
    private void initComponent(){
        // 设置主窗口背景为浅黄色
        getContentPane().setBackground(new Color(255, 250, 205)); // 浅黄色
        // System.out.println("initComponent");
    // 创建面板容器
    // 容器面板 文本框区域
        north = new JPanel();
        // 容器面板 按钮区域
        center = new JPanel();
        // 关键：给中间面板（按钮容器）也加一个边框，增强整体网格感
        center.setBorder(new LineBorder(Color.GRAY, 1)); 
        // 设置面板背景为浅黄色
        north.setBackground(new Color(255, 250, 205));
        center.setBackground(new Color(255, 250, 205));
        // 文本框 显示计算过程、结果或者日期时间
        field = new JTextField();
        // 设置输入框大小 首选尺寸
        field.setPreferredSize(new Dimension(700,150));
        // 禁用文字输入 禁止通过电脑键盘输入编辑
        field.setEditable(false);
        // 设置输入框中文字大小
        field.setFont(new Font("SansSerif",Font.BOLD,35));
        // 文本框加边框，与按钮区域区分
        field.setBorder(new LineBorder(Color.LIGHT_GRAY, 2));
        // 设置文本框背景为浅黄色（与窗口背景一致）
        field.setBackground(new Color(255, 250, 205));
        // 设置文本颜色为黑色，确保文字清晰可见
        field.setForeground(Color.BLACK);
        // 按钮文本内容
        String[] str = new String[]{
            "7","8","9","+",
            "4","5","6","-",
            "1","2","3","×",
            "0",".","=","÷",
            "C","B","T","S",
            "sin", "cos", "tan", "log",
            "sqrt", "(", ")", "π"

        };
         // 按钮数组
        btns = new JButton[str.length];
        for(int i = 0;i < btns.length;i++){
            btns[i] = new JButton(str[i]);
            btns[i].setFont(new Font("SansSerif",Font.BOLD,25));
            // 关键：判断是否为科学按钮，若是则设置粉色背景
            if (isScientificButton(str[i])) {
                btns[i].setBackground(Color.PINK); // 设置粉色背景
                btns[i].setOpaque(true); // 确保背景色生效
                // 关键：添加黑色边框，让按钮之间的"格子"显示
                btns[i].setBorder(new LineBorder(Color.BLACK, 1)); 
            } else {
                // 普通按钮也加浅灰色边框，保持整体风格统一
                btns[i].setBorder(new LineBorder(Color.LIGHT_GRAY, 1));
            }
            
        }
         // 将组件放到窗口中
        // 设置布局管理器
        // north 顶部面板 使用 BorderLayout 边界布局

        north.setLayout(new BorderLayout());
          // center 中间面板 使用6行4列 的 网格布局
        center.setLayout(new GridLayout(6,4,2,2));// 增加网格间距（2像素）
         // 组件设置
        // field 文本框 添加到 north 顶部面板 使用 BorderLayout 居中
        north.add(field,BorderLayout.CENTER);
          // btns 按钮 添加到 center 面板 增强for循环
        for (JButton btn : btns){
             // 通过遍历将所有按钮添加到 center 面板
            center.add(btn);
        }
        // 将 north 顶部面板 添加到窗口中
        add(north,BorderLayout.NORTH);
         // 将 center 面板 添加到窗口中
        add(center);
        // 绑定按钮的监听事件
        for (JButton btn : btns){
            btn.addActionListener(this);

        }
    }

     // 播放背景音乐
    private void playBackgroundMusic() {
        try {
            // 音频文件路径（请替换为你的音频文件路径）
            String musicPath = "C:\\Users\\mlqs_\\Desktop\\Test3\\Hopes and Dreams - nokbient.wav"; // 支持WAV格式
            File musicFile = new File(musicPath);
            
            // 检查文件是否存在
            if (!musicFile.exists()) {
                System.out.println("背景音乐文件不存在: " + musicPath);
                return;
            }

            // 加载音频文件
            AudioInputStream audioStream = AudioSystem.getAudioInputStream(musicFile);
            backgroundMusic = AudioSystem.getClip();
            backgroundMusic.open(audioStream);
            
            // 设置循环播放
            backgroundMusic.loop(Clip.LOOP_CONTINUOUSLY);
            System.out.println("背景音乐播放中...");

        } catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) {
            System.out.println("背景音乐播放失败: " + e.getMessage());
            e.printStackTrace();
        }
    }

    // 停止背景音乐（窗口关闭时调用）
    @Override
    public void dispose() {
        if (backgroundMusic != null && backgroundMusic.isRunning()) {
            backgroundMusic.stop();
            backgroundMusic.close();
            System.out.println("背景音乐已停止");
        }
        super.dispose();
    }

    // 辅助方法：判断按钮是否为科学功能按钮
    private boolean isScientificButton(String text) {
        for (String btn : SCIENTIFIC_BUTTONS) {
            if (btn.equals(text)) {
                return true;
            }
        }
        return false;
    }

    // 处理按键文本
   /* 处理按钮点击事件，根据按钮文本执行相应操作
     @param text 按钮上显示的文本*/
    private void setBtnText(String text){
        //System.out.println("setBtnText:"+text);
        //System.out.println("text length"+text.length());
        //System.out.println("field text length:" + field.getText());
        String currentText = field.getText();// 获取当前文本框内容
         // 小数点自动补零逻辑
        if (text.equals(".")) {
            if (currentText.isEmpty() || isOperator(currentText.substring(currentText.length() - 1))) {
                field.setText(currentText + "0.");
            return;
        }
    }
        
        // 新增：输入验证逻辑
        if (isOperator(text)) {
            // 操作符输入验证
            if (!canAppendOperator(currentText)) {
                return; // 不满足条件则忽略此次输入
            }
        } else if (text.equals(".")) {
            // 小数点输入验证
            if (!canAppendDecimal(currentText)) {
                return; // 不满足条件则忽略此次输入
            }
         } else if (text.equals(".")) {
            if (!canAppendDecimal(currentText)) {
                return;
            }
        } else if (isFunction(text)) {
            if (!currentText.endsWith("(")) {
                field.setText(currentText + text + "(");
    }       else {
                field.setText(currentText + text + "("); // 避免重复括号
    }
                isInScientificMode = true;
            return;

         // 新增：处理π常量  
        } else if (text.equals("π")) {
            field.setText(currentText + Math.PI);
            return;  


        }
        switch(text){
            case "C":
            //清空功能
            field.setText("");
            isInScientificMode = false;
                //System.out.println("C");
                break;
            case "B":
            // 退格功能：删除最后一个字符
                if (!currentText.isEmpty()) {
                    // 截取除最后一个字符外的所有内容
                    field.setText(currentText.substring(0, currentText.length() - 1));
                }
                break;
            case "T":
                // 时间/日期切换功能：如果当前显示日期则切换到时间，否则显示日期
                if (currentText.contains("日期")) {
                    SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
                    field.setText(timeFormat.format(new Date()));
                } else {
                    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                    field.setText("当前日期: " + dateFormat.format(new Date()));
                }
                break;
            case "S":
             // 科学计算功能：如果文本为空则提示，否则执行科学计算
                if (currentText.isEmpty()) {
                    field.setText("科学计算: ");
                } else {
                    performScientificOperation(currentText);
             }
                //System.out.println("S：自定义功能");
                break;
            case "=":
            //System.out.println("=:执行计算功能")；
            calculate();
                break;
            default:
             // 按下数字键或者运算符 要把文字放到输入框中
             // 如果之前输入框中有文字 做拼接
                String oldText = field.getText();
                field.setText(oldText+text);
                break;
        }
    }
    // 实现 ActionListener 接口中的抽象方法 处理事件

    // 执行科学计算的方法
    private void performScientificOperation(String expression) {
        try {
            // 移除可能的"科学计算: "前缀
            if (expression.startsWith("科学计算: ")) {
                expression = expression.substring(6);
            }

            // 将数学函数替换为JavaScript引擎可识别的格式（如sin -> Math.sin）
            expression = replaceFunctions(expression);
            // 将角度转换为弧度（JavaScript三角函数使用弧度计算）
            expression = convertDegreesToRadians(expression);

            // 创建脚本引擎管理器
            ScriptEngineManager manager = new ScriptEngineManager();
            // 获取JavaScript脚本引擎
            ScriptEngine engine = manager.getEngineByName("javascript");

            // 执行计算表达式
            Object result = engine.eval(expression);
            // 保存计算结果并显示到文本框
            lastResult = result.toString();
            field.setText(lastResult);
            // 退出科学计算模式
            isInScientificMode = false;

        } catch (Exception e) {
            // 计算出错时显示错误信息
            field.setText("科学计算错误");
            e.printStackTrace();
        }
    }

    // 将函数名替换为JavaScript引擎可识别的格式
    private String replaceFunctions(String expr) {
        expr = expr.replace("sin", "Math.sin")
                   .replace("cos", "Math.cos")
                   .replace("tan", "Math.tan")
                   .replace("log", "Math.log")
                   .replace("sqrt", "Math.sqrt");
        return expr;
    }

    // 将角度值转换为弧度值（用于三角函数计算）
    private String convertDegreesToRadians(String expr) {
        // 匹配三角函数中的角度值（例如sin(45)中的45）
        String pattern = "(sin|cos|tan)\\(([^)]+)\\)";
        java.util.regex.Pattern r = java.util.regex.Pattern.compile(pattern);
        java.util.regex.Matcher matcher = r.matcher(expr);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            String func = matcher.group(1);
            String param = matcher.group(2);
        // 对参数进行弧度转换（参数可能是表达式，需先计算）
            String radiansExpr = "Math.PI / 180 * (" + param + ")";
            matcher.appendReplacement(sb, func + "(" + radiansExpr + ")");
    }
            matcher.appendTail(sb);
            return sb.toString();
}
            

    // 判断字符是否为操作符（+、-、×、÷）
    private boolean isOperator(String text) {
        return text.equals("+") || text.equals("-") || text.equals("×") || text.equals("÷");
    }

    // 判断是否为科学函数（sin、cos、tan、log、sqrt）
    private boolean isFunction(String text) {
        return text.equals("sin") || text.equals("cos") || text.equals("tan") || 
               text.equals("log") || text.equals("sqrt");
    }

    // 判断是否可以添加操作符
    private boolean canAppendOperator(String currentText) {
        if (currentText.isEmpty()) {
            return false; // 表达式为空时不能添加操作符
        }

        // 获取最后一个字符
        char lastChar = currentText.charAt(currentText.length() - 1);
        // 如果最后一个字符是操作符或小数点，则不能添加操作符
        return !(isOperator(String.valueOf(lastChar)) || lastChar == '.');
    }

    // 判断是否可以添加小数点
    private boolean canAppendDecimal(String currentText) {
        if (currentText.isEmpty()) {
            return true; // 空表达式可以添加小数点（后续会补0）
        }

        // 获取最后一个字符
        char lastChar = currentText.charAt(currentText.length() - 1);
        // 如果最后一个字符是操作符，则不能直接添加小数点
        if (isOperator(String.valueOf(lastChar))) {
            return false;
        }

        // 查找当前数字部分是否已经有小数点
        // 从后往前找到最近的操作符或字符串开头
        int lastOpIndex = -1;
        for (int i = currentText.length() - 1; i >= 0; i--) {
            if (isOperator(String.valueOf(currentText.charAt(i)))) {
                lastOpIndex = i;
                break;
            }
        }

        // 截取当前数字部分
        String currentNumber = currentText.substring(lastOpIndex + 1);
        // 如果当前数字部分已经包含小数点，则不能再添加
        return !currentNumber.contains(".");
    }


    //执行计算功能
    private void calculate() {
    String expression = field.getText();
    if (expression.isEmpty()) return;
    
    // 先替换乘除符号
    expression = expression.replace("×", "*").replace("÷", "/");
    
    // 处理科学函数（替换为Math.xxx格式）
    expression = replaceFunctions(expression);
    // 处理角度转弧度
    expression = convertDegreesToRadians(expression);
    
    // 执行计算
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("javascript");
    try {
        Object result = engine.eval(expression);
        field.setText(result.toString());
    } catch (ScriptException e) {
        field.setText("运算错误");
        e.printStackTrace();
    }
}

    
    @Override
    public void actionPerformed(ActionEvent a){
        // 此处可以捕获到 ActionListener 监听的事件
        // System.out.println(a.getSource());
        // 获取被点击的按钮 或者按钮的文字
        //System.out.println(a);
        Object source = a.getSource();
        if (source instanceof JButton){
            JButton btn = (JButton) source;
            String text = btn.getText();
             // System.out.println(text);
            // 处理对应按键文字
            setBtnText(text);
           // System.out.println(text);
        }
        
    }
    public static void main(String[] args){
    // System.out.println("hello,Calculator");
    // 创建类的对象 - 线程不安全
    // new Calculator();
    // 是 Java Swing 开发中确保线程安全的核心代码
    // EDT 线程保障
    // Swing 要求所有 GUI 操作（组件创建、属性修改、事件处理）必须在 Event Dispatch Thread (EDT) 执行。这里代码通过
    // invokeLater 将 Calculator 类的实例化操作（即 GUI 初始化）提交到 EDT 队列，确保线程安全。
    // 异步执行机制
    // invokeLater 会将传入的 Runnable 任务（此处为 Calculator::new 方法引用）放入 EDT 的任务队列，由 EDT
    // 按顺序执行。这避免了直接操作 GUI 导致的线程竞争问题。
        SwingUtilities.invokeLater(Calculator::new);
        //System.out.println("hello,Calculator");
        //new Calculator();
    }
    
}
